package com.corecloud.security;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.corecloud.entity.SysPermissionEntity;
import com.corecloud.entity.SysPermissionRoleEntity;
import com.corecloud.mapper.mysql.SysPermissionMapper;
import com.corecloud.mapper.mysql.SysPermissionRoleMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.*;

@Service
public class MyFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {

	@Autowired
	private SysPermissionMapper permissionMapper;
	@Autowired
	private SysPermissionRoleMapper permissionRoleMapper;

	public static Map<String, Collection<ConfigAttribute>> resourceMap = null;

	/**
	 * 在Web服务器启动时,提取系统中的所有权限
	 */
	public void loadResourceDefine() {
		//获取所有资源与对应角色
		List<SysPermissionRoleEntity> rolePermissionsList = permissionRoleMapper.selectList(new QueryWrapper<SysPermissionRoleEntity>().eq("permission_state",1));
		List<SysPermissionEntity> permissionsList = permissionMapper.selectList(new QueryWrapper<SysPermissionEntity>().eq("is_valid",1));

		//拼装角色与对应url资源
		resourceMap = new HashMap<String, Collection<ConfigAttribute>>();
		for(int i = 0;i<permissionsList.size();i++){
			SysPermissionEntity p = permissionsList.get(i);
			if(StringUtils.isEmpty(p.getUrl())){
				continue;
			}
			Collection<ConfigAttribute> atts = new ArrayList<ConfigAttribute>();
			for(int j = 0;j<rolePermissionsList.size();j++){
				SysPermissionRoleEntity rp = rolePermissionsList.get(j);
				if(p.getUrl().equals(rp.getPermissionUrl())){
					atts.add(new SecurityConfig("ROLE_"+rp.getRoleCode()));
				}
			}
			if(atts != null && atts.size() > 0){
				resourceMap.put(p.getUrl(), atts);
			}else{
				//未定义权限角色并已启用资源
				atts = new ArrayList<ConfigAttribute>();
				atts.add(new SecurityConfig("ROLE_UNDEFINED"));
				resourceMap.put(p.getUrl(), atts);
			}
		}
	}




	/**
	 * 此方法是为了判定用户请求的url 是否在权限表中,如果在权限表中,则返回给 decide 方法,用来判定用户是否有此权限。如果不在权限表中则放行。
	 * Accesses the {@code ConfigAttribute}s that apply to a given secure object.
	 *
	 * @param object the object being secured
	 * @return the attributes that apply to the passed in secured object. Should return an
	 * empty collection if there are no applicable attributes.
	 * @throws IllegalArgumentException if the passed object is not of a type supported by
	 *                                  the <code>SecurityMetadataSource</code> implementation
	 */
	@Override
	public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
		if(resourceMap ==null) loadResourceDefine();
		//object 中包含用户请求的 request 信息
		HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();
		AntPathRequestMatcher matcher;
		String resUrl;
		for(Iterator<String> iter = resourceMap.keySet().iterator(); iter.hasNext(); ) { 
			resUrl = iter.next();
			matcher = new AntPathRequestMatcher(resUrl);
			if(matcher.matches(request)) {
				return resourceMap.get(resUrl);
			}
		}
		return null;
	}


	/**
	 * If available, returns all of the {@code ConfigAttribute}s defined by the
	 * implementing class.
	 * <p>
	 * This is used by the {@link } to perform startup time
	 * validation of each {@code ConfigAttribute} configured against it.
	 *
	 * @return the {@code ConfigAttribute}s or {@code null} if unsupported
	 */
	@Override
	public Collection<ConfigAttribute> getAllConfigAttributes() {
		return null;
	}

	/**
	 * Indicates whether the {@code SecurityMetadataSource} implementation is able to
	 * provide {@code ConfigAttribute}s for the indicated secure object type.
	 *
	 * @param clazz the class that is being queried
	 * @return true if the implementation can process the indicated class
	 */
	@Override
	public boolean supports(Class<?> clazz) {
		return true;
	}
}
